using System;
using System.Collections.Generic;
using CLELCore.Assembly.CLELInstructions;

namespace opt.Analysis.Branch
{
	public class BranchTable
	{
		private List<Int32> indexes;
		private List<Int32> switch_indexes;
		private List<Byte> opcodes;
		private List<Int32> from;
		private List<Int32> offset;

		public BranchTable(List<CLELInstruction> code)
		{
			indexes = new List<Int32>();
			switch_indexes = new List<Int32>();
			opcodes = new List<Byte>();
			from = new List<Int32>();
			offset = new List<Int32>();

			if(code != null)
			{
				int from_offset = 0;
				int br_offset = 0;
				CLELInstruction inst;
				int i,code_count = code.Count;
				int j,switch_count;
				List<Int32> switch_offsets;
				Switch sw;
				for(i = 0;i < code_count;i++)
				{
					inst = code[i];
					if(BranchUtilities.IsBranch(inst))
					{
						indexes.Add(i);
						switch_indexes.Add(-1);
						opcodes.Add(inst.Opcode);
						BranchUtilities.GetBranchOffset(inst,ref br_offset);
						from.Add(from_offset+inst.Length);
						//from.Add(from_offset);
						offset.Add(br_offset);
					}
					else if(inst.Opcode == CLELOpcode.SWITCH)
					{
						sw = (Switch)inst;
						switch_offsets = sw.Params;
						switch_count = switch_offsets.Count;
						for(j = 0;j < switch_count;j++)
						{
							indexes.Add(i);
							switch_indexes.Add(j);
							opcodes.Add(inst.Opcode);
							from.Add(from_offset+inst.Length);
							//from.Add(from_offset);
							offset.Add(switch_offsets[j]);
						}
					}
					from_offset += inst.Length;
				}
			}
		}

		public List<BranchKey> GetKeys()
		{
			List<BranchKey> keys = new List<BranchKey>();
			int i,count = indexes.Count;
			for(i = 0;i < count;i++)
			{
				keys.Add(new BranchKey(indexes[i],switch_indexes[i]));
			}

			return keys;
		}

		private int FindIndex(BranchKey key)
		{
			int i,keys_count = indexes.Count;
			for(i = 0;i < keys_count;i++)
			{
				if((indexes[i] == key.Index) && (switch_indexes[i] == key.OffsetIndex))
					return i;
			}
			return -1;
		}
		
		public byte GetOpcode(BranchKey key)
		{
			int index = FindIndex(key);
			if(index >= 0)
				return opcodes[index];
			return 255;
		}

		public int GetFrom(BranchKey key)
		{
			int index = FindIndex(key);
			if(index >= 0)
				return from[index];
			return -1;
		}

		public int GetOffset(BranchKey key)
		{
			int index = FindIndex(key);
			if(index >= 0)
				return offset[index];
			return -1;
		}

		public List<Int32> GetSwitchOffsets(BranchKey key)
		{
			List<Int32> off = new List<Int32>();
			int i,count = indexes.Count;
			for(i = 0;i < count;i++)
			{
				if(indexes[i] == key.Index)
					off.Add(offset[i]);
			}
			return off;
		}

		public int GetTo(BranchKey key)
		{
			int from = GetFrom(key);
			if(from != -1)
				return from + GetOffset(key);
			return -1;
		}

		public void SetOffset(BranchKey key,int off)
		{
			int index = FindIndex(key);
			if(index != -1)
				offset[index] = off;
		}
	}
}
